package com.black.common.mybatis.service.impl;

import com.black.common.annotation.DataScope;
import com.black.common.mybatis.datascope.QueryDataScopeHandler;
import com.black.common.mybatis.service.DataScopeService;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.select.*;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;

/**
 * @author linwenfeng
 */
@Service
public class DataScopeServiceImpl implements DataScopeService {

    @Resource
    private QueryDataScopeHandler queryDataScopeHandler;

    @Override
    public String handler(String originalSql, String namespace) throws Exception {
        String className = namespace.substring(0, namespace.lastIndexOf("."));
        String methodName = namespace.substring(namespace.lastIndexOf(".") + 1);
        Class<?> aClass = Class.forName(className);
        Method[] methods = aClass.getMethods();
        DataScope dataScope = null;
        for (Method method : methods) {
            if (method.getName().equalsIgnoreCase(methodName)) {
                dataScope = method.getAnnotation(DataScope.class);
                break;
            }
        }
        if (dataScope == null) {
            dataScope = aClass.getAnnotation(DataScope.class);
            if (dataScope == null) {
                return originalSql;
            } else {
                if (dataScope.interceptMethods().length>0){
                    boolean contains = Arrays.asList(dataScope.interceptMethods()).contains(methodName);
                    if (!contains) {
                        return originalSql;
                    }
                }
            }
        }
        String powerSql = getPowerSql(dataScope);
        if (StringUtils.isEmpty(powerSql)) {
            return originalSql;
        }
        Select select = (Select) CCJSqlParserUtil.parse(originalSql);
        SelectBody selectBody = select.getSelectBody();
        String tableAlias = dataScope.tableAlias();
        if (!StringUtils.isEmpty(tableAlias)) {
            if (selectBody instanceof SetOperationList) {
                SetOperationList setBody = (SetOperationList) selectBody;
                List<SelectBody> selectList = setBody.getSelects();
                for (SelectBody body : selectList) {
                    PlainSelect plainSelect = (PlainSelect) body;
                    Expression where = plainSelect.getWhere();
                    Table tableInfo = (Table) plainSelect.getFromItem();
                    if (tableAlias.equalsIgnoreCase(tableInfo.getName())) {
                        continue;
                    }
                    if (where == null) {
                        where = CCJSqlParserUtil.parseCondExpression(powerSql);
                    } else {
                        where = new AndExpression(where, CCJSqlParserUtil.parseCondExpression(powerSql));
                    }
                    plainSelect.setWhere(where);
                }
            } else if (selectBody instanceof PlainSelect) {
                PlainSelect plainSelect = (PlainSelect) selectBody;
                FromItem fromItem = plainSelect.getFromItem();
                if (fromItem instanceof SubSelect) {
                    SubSelect subSelect = (SubSelect) fromItem;
                    SelectBody selectBody1 = subSelect.getSelectBody();
                    if (selectBody1 instanceof PlainSelect) {
                        setSqlWhere(powerSql, (PlainSelect) selectBody1);
                    }
                } else if (fromItem instanceof Table) {
                    Expression where = plainSelect.getWhere();
                    if (where == null) {
                        where = CCJSqlParserUtil.parseCondExpression(powerSql);
                    } else {
                        where = new AndExpression(where, CCJSqlParserUtil.parseCondExpression(powerSql));
                    }
                    plainSelect.setWhere(where);
                }
            }
        } else {
            setSqlWhere(powerSql, (PlainSelect) selectBody);
        }
        return select.toString();
    }

    private void setSqlWhere(String powerSql, PlainSelect selectBody) throws JSQLParserException {
        Expression where = selectBody.getWhere();
        if (where == null) {
            where = CCJSqlParserUtil.parseCondExpression(powerSql);
        } else {
            where = new AndExpression(where, CCJSqlParserUtil.parseCondExpression(powerSql));
        }
        selectBody.setWhere(where);
    }

    /**
     * 获取权限数据sql
     *
     * @param dataScope 数据权限注解
     * @return 权限数据sql
     */
    private String getPowerSql(DataScope dataScope) throws Exception {
        return queryDataScopeHandler.handler(dataScope);
    }

}
